<html lang="zh">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
	<script type="text/javascript" src="./rtc/adapter-latest.js"></script>
	<script type="text/javascript" src='./js/uni.webview.js'></script>
	<script type="text/javascript" src='./js/utils.js'></script>
	<script type="text/javascript" src='./js/jsonly.js'></script>
	<style>
		body{
			padding:0;
			margin:0;
			background-image: url('image/wallpaper.png');
			background-size: contain;
		}

		.webrtc-box{
		  background: #666;
		  border-radius: 6px;
		  width:100%;
		  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
		}
		.localvideo{
		    width:100vw;
		    height:100vh;
			object-fit: cover;
		}
		.remotevideo{
		   min-height: 160px;
		   width: 100px;
		   position: fixed;
		   top: 40px;
		   right: 15px;
		   z-index:10;
		   object-fit: cover;
		}
		
		.call-user-box{
			position:fixed;
			bottom: 20px;
			width:100%;
		}
		
		.call-user{
		  display: flex;
		  justify-content: center;
		  align-items: center;
		  flex-direction: column;
		  margin-bottom:50px;
		}
		.call-user .avatar{
		    width:60px;
		    height:60px;
		    object-fit: contain;
		    border-radius: 50%;
		    overflow: hidden;
		}
		.call-user .text{
			font-size:16px;
			margin-top:15px;
			color:#f6f6f6
		}
		
		.call-time{
			color:#f6f6f6;
			font-size: 24px;
			text-align: center;
		}
		
		.calling-button{
		   display: flex;
		   justify-content: space-around;
		   padding: 20px;
		}
		.calling-button .button{
		    display: flex;
		    flex-direction: column;
		    align-items: center;
		    justify-content: center;
		}
		.calling-button .button .image{
		    width:60px;
		    height:60px;
		    margin-bottom: 10px;
		}
		.calling-button .button .text{
		    color:#f6f6f6;
		}
		.calling-button .switch-btn .text{
			font-size:12px !important; 
		}
		.calling-button .button .image-icon{
		    width:40px;
		    height:40px;
		    margin-bottom: 10px;
		}
		
	</style>
	</head>
	<body>
		<div id="app">
			<div class="webrtc-box">
				<audio id="music1">
			        <source src="https://im.file.raingad.com/static/voice/calling.mp3">
			    </audio>
			    <video v-show="localStream && is_video" class="localvideo" ref="localvideo"  x5-video-player-fullscreen="true" autoplay x5-playsinline playsinline webkit-playsinline @click="displayBtn = !displayBtn" poster="./image/wallpaper.png"></video>
			    <video v-show="remoteStream && is_video" class="remotevideo" ref="remotevideo" x5-video-player-fullscreen="true" autoplay x5-playsinline playsinline webkit-playsinline @click="changeVideo()" poster="./image/wallpaper.png"></video>
			    <div class="call-user-box" v-if="displayBtn">
			      <div class="call-user" v-if="contact">
			        <img class="avatar" v-if="status!=2 || !is_video" :src="contact.avatar" alt="">
			        <div class="text"> 
			          <b v-if="!is_video && status==2">{{contact.displayName}}</b>
			          <span v-if="status!=2">
			            <span v-if="status==3"> {{contact.displayName}} 正在请求与您{{is_video ? '视频' : '语音'}}通话</span>
			            <span v-else>您正对 <b>{{contact.displayName}}</b> 发起{{is_video ? '视频' : '语音'}}通话</span>   
			          </span>
					  
			        </div>
			      </div>
				  <div class="call-time" v-if="callTime && status==2">
					  {{setCallTime()}}
				  </div>
			      <div class="calling-button">
			        <div class="button" v-if="status!=0" >
			          <img class="image" src="./image/guaduan.png" @click="hangup(true)"/>
			          <div class="text">挂断</div> 
			        </div>
			        <div class="button switch-btn" v-if="status<3" >
			          <img class="image-icon" :src="'./image/voice'+(voiceStatus ? '' : '-off')+'.png'" @click="switchVoice()"/>
					  <div class="text">{{ voiceStatus ? '关闭' : '开启'}}麦克风</div> 
			        </div>
					<div class="button" v-if="status==3" >
					  <img class="image" src="./image/jieting.png" @click="answer()"/>
					  <div class="text">接听</div>
					</div>
			        <div class="button switch-btn" v-if="status<3" >
						<template v-if="is_video">
							<img class="image-icon" :src="'./image/video.png'" @click="exchangeVideo()"/>
							<div class="text">切换摄像头</div> 
						</template>
						<template v-else>
						  <img class="image-icon" :src="'./image/speaker'+(speaker ? '' : '-off')+'.png'" @click="speakBtn()"/>
						  <div class="text">{{ speaker ? '关闭' : '开启'}}扬声器</div> 
						</template>
			        </div>
			      </div>
			    </div>
			  </div>
		</div>
	</body>
	<script type="text/javascript" src='./js/vue.js'></script>
	<script>
		const params=parseUrl(window.location.href);
		const opt=JSON.parse(decodeURIComponent(params.stun));
		const config = {
			'iceServers': [{
				'urls': ['stun:stun.xten.com', 'stun:stun.l.google.com:19302', 'stun:stun1.l.google.com:19302',
				  'stun:stun2.l.google.com:19302', 'stun:stun3.l.google.com:19302', 'stun:stun4.l.google.com:19302'
				]
			  },{
				  'urls': opt.stun ? opt.stun : ['stun:stun.callwithus.com'], // 自己搭建服务器地址
				  "username":opt.stunUser ? opt.stunUser : '',
				  "credential":opt.stunPass ? opt.stunPass : ''
			  }
			],
		  };
		const Counter = {
			data() {
				return {
					displayBtn:true,
					platform:params.platform,
					status: 0,          //状态0，默认，1：拨号中，2通话中，3来电中，4忙线
					pc: null,         //pc实力化
					localVideo: "",    //本地视频的DOM
					remoteVideo: "",   //远程视频的DOM
					remoteStream: null, // 远端视频流
					localStream: null,  // 本地视频流
					is_video: 0,        //是否为视频通话
					videoStatus: true,  //视频开启状态
					voiceStatus: true,  //语音开启状态
					cutdown: 40,        //拨号超时
					timer: null,         //计时器
					offerParams:{},
					plus:null,
					streamType:1, //视频通话展示方式
					facingMode:'user',//前置摄像头还是后置摄像头 user-前置 environment-后置
					headset : true, //麦克风 打开true 关闭false,
					senders: null, // 数据流
					speaker:true, // 听筒 false 扬声器true
					callTime:0, //通话时间
					callTimeDis:'', //通话时间展示
					timerIntervalId:null,  //通话计时器
					contact:{
						id:params.target_id,
						displayName:params.name,
						avatar:params.avatar
					}
				};
			},
			mounted() {
				this.pc = new RTCPeerConnection(config);
				this.pc.ontrack = (event) => {
					console.log(event,'接收视频流');
					if(this.localVideo){
						this.remoteStream = event.streams[0];
						setTimeout(()=>{
							this.streamType=2;
						},50)
						
					}
					
				};
				
				if (this.platform === 'app') {
				  document.addEventListener('plusready', () => {
				    console.log('设置扬声器')
				    this.plus = plus.audio.createPlayer();
				    this.plus.setRoute(plus.audio.ROUTE_SPEAKER);
				  });
				}
				
			    this.localVideo = this.$refs.localvideo;
			    this.remoteVideo = this.$refs.remotevideo;
				
				window.addEventListener('message', (e) => {
				  this.callMessagecallback(e)
				}, false);
				
				window.getUniAppMessage = (arg) => {
				  const data = {
				    data: jsonly(arg)
				  }
				  this.callMessagecallback(data)
				}
				this.is_video = params.type==1 ? true : false;
				
				this.offerParams = this.is_video ? {
				  offerToRecieveAudio: 1,
				  offerToRecieveVideo: 1
				} : {
				  offerToRecieveAudio: 1,
				  offerToRecieveVideo: 0
				}
				this.status=params.status
				// 如果状态为1,表示拨打电话，并且calling状态为1的时候才是直接拨打；
				if(this.status==1){
					if(params.calling==1){
						this.called(this.is_video)
					}
				}else{
					this.playMusicCall('state');
				}
			},
			watch:{
				streamType(val){
					// 切换镜头位置
					if(val==1){
						this.localVideo.srcObject = this.localStream;
						this.remoteVideo.srcObject = this.remoteStream;
						this.localVideo.muted=true;
						this.remoteVideo.muted=false;
					}else{
						this.localVideo.srcObject = this.remoteStream;
						this.remoteVideo.srcObject = this.localStream;
						this.localVideo.muted=false;
						this.remoteVideo.muted=true;
					}
				}
			},
			methods: {
				// 开始通话计时
			    startTime() {
					this.timerIntervalId=setInterval(()=>{
						this.callTime++
					},1000)
			    },
				// 设置通话时间
				setCallTime(){
					let time=this.callTime;
					const hours = Math.floor(time / 3600);
					const minutes = Math.floor((time - (hours * 3600)) / 60);
					const seconds = time - (hours * 3600) - (minutes * 60);
					return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
				},
			    // 视频电话初始化本地视频
			    initLocalStream(call_id, is_video) {
					let video=is_video;
					if(is_video){
						video = {
						  width: window.screen.height,
						  height: window.screen.width
						}
					}
				    navigator.mediaDevices.getUserMedia({
				        video: video,
				        audio: {echoCancellation: true}
				    }).then((stream) => {
				        this.localStream = stream;
					    // 同步音频
						 stream.getTracks().forEach((track) => {
							this.pc.addTrack(track, stream);
						});
				        this.localVideo.srcObject = this.localStream;
						// 把自己的视频静音
						this.localVideo.muted = true;
					    if(call_id){
							this.postMsg({
								event:'calling',
								status:3,
								code:901
							});
							  // 计时器，如果一段时间没有接听则自动挂断
							this.timer = setInterval(() => {
							    this.cutdown--;
							    if (this.cutdown == 0) {
							        this.hangup(true);
							    }
							}, 1000)
					    }else{
						    // 告诉对方已经接听电话
						    this.postMsg({ event: 'acceptRtc',code:904});
					    }
				      // 监听远程媒体流
				    }).catch((e) => {
						this.postMsg({
						  event: 'mediaDevices',
						})
				    });
			    },
			    // 拨打电话
			    called(is_video) {
			      this.is_video = is_video;
			      this.initLocalStream(true, is_video);
			      this.playMusicCall('state');
			    },
			    // 接听电话
			    answer() {
					this.status = 2;
			        this.initLocalStream(false, this.is_video);
			        this.playMusicCall('close');
					this.startTime();
			    },
			    // 挂断电话
			    hangup(btn) {
			      clearInterval(this.timer);
				  clearInterval(this.timerIntervalId);
				  if(this.status!=2){
					  this.playMusicCall('close');
				  }
			      if (this.status) {
			        this.closeLocalMedia();  //关闭本地媒体
			        this.remoteStream=null;  //关闭远程媒体
			      }
				  // 通话取消
				  let code=902;
				  // 通话中挂断
				  if(this.status==2 ){
					  code=906
					  // 拒绝挂断
				  }else if(this.status==3 ){
					  code=903
					  //对方忙线中
				  }else if(this.status==4 ){
					  code=907
				  }
				  this.postMsg({
				  	event:'hangup',
					isbtn:btn,
					callTime:this.callTime,
					code:code
				  })
			    },
			    // 关闭本地媒体
			    closeLocalMedia() {
			      if (this.localStream && this.localStream.getTracks()) {
			        this.localStream.getTracks().forEach((track) => {
			          track.stop();
			        });
			      }
			      this.localStream = null;
			    },
			    // 打开或关闭声音
			    switchVoice() {
			      if (this.localStream == null) {
			        alert('请打开音视频');
			        return false;
			      }
			      const tracks = this.localStream.getTracks();
			      if (this.voiceStatus) {
			        tracks.forEach(track => {
			          if (track.kind === 'audio') {
			            track.enabled = false
			          }
			        });
			        this.voiceStatus = false;
			      } else {
			        tracks.forEach(track => {
			          if (track.kind === 'audio') {
			            track.enabled = true
			          }
			        });
			        this.voiceStatus = true;
			      }
			    },
			    // 临时开、关视频
			    switchVideo() {
			      if (this.localStream == null) {
			        alert('请打开音视频');
			        return false;
			      }
			      const tracks = this.localStream.getTracks();
			      if (this.videoStatus) {
			        tracks.forEach(track => {
			          if (track.kind === 'video') {
			            track.enabled = false
			          }
			        });
			        this.videoStatus = false;
			      } else {
			        tracks.forEach(track => {
			          if (track.kind === 'video') {
			            track.enabled = true
			          }
			        });
			        this.videoStatus = true;
			      }
			    },
				// 切换前后摄像头
				exchangeVideo() {
				    this.localStream.getTracks().forEach(track => track.stop());
				    if (this.facingMode == 'user') this.facingMode = 'environment'
				    else this.facingMode = 'user'
				    navigator.mediaDevices.getUserMedia({
				        video: {
							width: window.screen.height,
							height: window.screen.width,
							facingMode: {
							  exact: this.facingMode
							}
					    }, 
					    audio: {
						    echoCancellation: true,
						}
				    }).then((mediastream) =>  {
						this.senders = this.pc.getSenders()
						let videoTrack = mediastream.getVideoTracks()[0];
						let audioTrack = mediastream.getAudioTracks()[0];
						var sender = this.senders.find((s) => {
						  return s.track.kind == 'video';
						});
						var sender2 = this.senders.find((s) => {
						  return s.track.kind == 'audio';
						});
						sender.replaceTrack(videoTrack);
						sender2.replaceTrack(audioTrack);
						if (this.streamType === 2) this.remoteVideo.srcObject = mediastream;
						else this.localVideo.srcObject = mediastream
						this.localStream = mediastream
						if(this.voiceStatus==false){
							this.voiceStatus=true;
							this.switchVoice();
						}
						if(this.speaker){
							this.speaker = !this.speaker
						}else{
							this.speaker = !this.speaker
						}
				  })
				},
			    // 播放响铃
			    playMusicCall(type) {
			      var audio = document.getElementById("music1");
				  if(type=='close' && !audio.paused){
					  audio.pause(); // 暂停
					  return;
				  }
			      if (type === "state") {
			          audio.loop = true;
			      } else {
			          audio.loop = false;
			      }
				  
				  if (audio.paused) {
					  audio.play(); // 播放
				  } else {
					  audio.pause(); // 暂停
				  }
			    },
				// 向uniapp发送消息，页面通讯
				postMsg(data) {
				  if (this.platform === 'app') {
				    uni.postMessage({
				      data: data
				    })
				  } else {
				    window.parent.postMessage(data)
				  }
				},
				// 接收websocket发送过来的消息,由uniapp接收后传输到当前页面
				callMessagecallback(msg){
					let e=msg.data;
					switch (e.event) {
						case "calling":
							console.log('发起通话...');
							this.called(this.is_video);
							break;
						case "hangup":
							this.hangup(false);
							break;
						case "busy":
							this.status=4;
						    this.hangup(false);
							break;
						case "acceptRtc": //已经接听，创建offer并发送
							this.status = 2;
							clearInterval(this.timer);
							this.startTime();
							this.playMusicCall();
							this.createOffer()
							break;
						case "turndown":
						  break;
						case "answer":
							//同步answer信息...
							this.pc.setRemoteDescription(new RTCSessionDescription({
							  type: 'answer',
							  sdp:  e.sdp
							}));
							break;	
						case "iceCandidate":
							setTimeout(()=>{
								// 添加ice完成通话连接
								  if (typeof(e.iceCandidate) === 'object') {
									this.pc.addIceCandidate(new RTCIceCandidate(e.iceCandidate));
								  } else {
									this.pc.addIceCandidate(new RTCIceCandidate(JSON.parse(e.iceCandidate)));
								  }
							},100)
							break;
						case "offer":
						  this.pc.setRemoteDescription(new RTCSessionDescription({
							type: 'offer',
							sdp: e.sdp
						  }));
						  this.createAnswer();
							break;
						
					}
				},
				// 创建offer-sdp
				createOffer() {
				  this.pc.createOffer(this.offerParams).then((offer) => {
				  this.pc.setLocalDescription(offer);
				    this.postMsg({
				      event: 'offer',
				      sdp: offer.sdp
				    }, '*');
				  });
				  // 创建offer需要监听ice流
				  this.onicecandidate();
				},
				// 创建应答sdp
				createAnswer() {
				  this.pc.createAnswer(this.offerParams).then((answer) => {
				    this.pc.setLocalDescription(answer);
				    this.postMsg({
				      event: 'answer',
				      sdp: answer.sdp
				    }, '*');
				    this.onicecandidate();
				  });
				},
				onicecandidate(){
					this.pc.onicecandidate = (event) => {
					  var iceCandidate = event.candidate;
					  if (iceCandidate) {
					    this.postMsg({
					      event: 'iceCandidate',
					      iceCandidate: JSON.parse(JSON.stringify(iceCandidate))
					    }, '*');
					  }
					};
				},
				 //切换视频显示位置
				changeVideo(){
					this.streamType==1 ? this.streamType=2 : this.streamType=1;
				},
				//打开关闭扬声器 h5端就是静音 ROUTE_EARPIECE 听筒 ROUTE_SPEAKER 扬声器
				speakBtn() {
						if (this.speaker) { //扬声器 => 听筒    
						this.speaker = !this.speaker
						if (this.platform === 'h5') {
						  this.localVideo.muted = true
						}
						if (this.platform === 'app') {
						  this.plus.setRoute(plus.audio.ROUTE_EARPIECE);
						}
					} else { //听筒 => 扬声器
						this.speaker = !this.speaker
						if (this.platform === 'h5') {
						  this.localVideo.muted = false
						}
						if (this.platform === 'app') {
						  this.plus.setRoute(plus.audio.ROUTE_SPEAKER);
						}
					}
				}
			  }
		}
	
		const app = Vue.createApp(Counter);
		app.mount('#app');
	</script>
	</html>